AWS OrganizationsのOUおよび各アカウントに直接アタッチされているSCPの一覧をAWS CLIで出力する

AWS OrganizationsのOUおよび各アカウントに直接アタッチされているSCPの一覧をAWS CLIで出力する

Clock Icon2024.10.29

はじめに

AWS Organizations のマルチアカウント環境において、リージョン制限を解除して作業を行うため、一時的にSCP(サービスコントロールポリシー)をデタッチし、作業完了後に再度アタッチする必要が生じることがありました。

このような操作を行う際は、作業前後で同一のSCPが正確に適用されていることを確認することが重要です。

本記事では、組織単位(OU)および各アカウントに直接アタッチされているSCPの一覧を取得し、デタッチ前後で設定内容が一致していることを確認する方法について解説します。

OU階層を出力するコマンド

AWS Organizations管理アカウントからコマンドを実行します。

まず、AWS Organizations管理アカウントからOU階層を確認します。

この手順は必須ではありませんが、OUとアカウント構成を把握するのに役立ちます。

以下のコマンドをAWS CloudShellで実行します。

bash -c '
print_ou_tree() {
    local parent_id=$1 prefix=$2
    # 親OUの直下にある子OUのリストを取得
    local ous=$(aws organizations list-organizational-units-for-parent --parent-id $parent_id --query "OrganizationalUnits[*].[Id,Name]" --output text)
    local total_ous=$(echo "$ous" | wc -l)
    local current_ou=0

    echo "$ous" | while read -r ou_id ou_name; do
        [ -z "$ou_id" ] && continue
        ((current_ou++))

        # 現在の親OUの直下にある最後の子OUかどうかを判断
        if [ $current_ou -eq $total_ous ]; then
            local branch="└──" # 現在の階層レベルの最後の子OU
            local new_prefix="$prefix    "
        else
            local branch="├──" # 現在の階層レベルの中間の子OU
            local new_prefix="$prefix│   "
        fi

        # OUの名前を表示
        echo "$prefix$branch $ou_name"

        # 子OUを再帰的に処理
        print_ou_tree $ou_id "$new_prefix"
    done
}

echo "Organization Structure:"
echo "======================="
echo ""

# ルートOUのIDを取得
root_id=$(aws organizations list-roots --query "Roots[0].Id" --output text)
echo "Root"
# ルートOUから開始して、組織構造を再帰的に表示
print_ou_tree $root_id ""
'

私の環境では、以下の出力結果が得られました。

コマンド出力結果
AWS Organizations OU Hierarchy
==============================

Root
├── A_OU
├── B_OU
└── C_OU
    ├── test2_OU
    │   └── test2-1_OU
    └── test1_OU
        └── test1-2_OU
            └── test1-2-3_OU
                └── test1-2-3-4_OU

直接アタッチされているSCPを出力するコマンド

次に、OUおよび各アカウントに直接アタッチされているSCP一覧を出力するコマンドを実行します。
出力結果は、scp_before.txtファイルに保存します。

bash -c '
get_scps() {
    # SCPのリストを取得し、カンマ区切りに変換
    aws organizations list-policies-for-target --target-id $1 --filter SERVICE_CONTROL_POLICY --query "Policies[*].Name" --output text | tr "\t" ", "
}

print_ou_tree() {
    local parent_id=$1 prefix=$2
    local ous=$(aws organizations list-organizational-units-for-parent --parent-id $parent_id --query "OrganizationalUnits[*].[Id,Name]" --output text)
    local total_ous=$(echo "$ous" | wc -l)
    local current_ou=0

    echo "$ous" | while read -r ou_id ou_name; do
        [ -z "$ou_id" ] && continue
        ((current_ou++))

        # 現在の親OUの直下にある最後の子OUかどうかを判断
        if [ $current_ou -eq $total_ous ]; then
            local branch="└──" # 現在の階層レベルの最後の子OU
            local new_prefix="$prefix    "
        else
            local branch="├──" # 現在の階層レベルの中間の子OU
            local new_prefix="$prefix│   "
        fi

        # OUの名前を表示
        echo "$prefix$branch $ou_name"

        # SCPを取得して表示
        local scps=$(get_scps $ou_id)
        echo "$new_prefix""Attached SCPs: ${scps:-None}"

        # 子OUを再帰的に処理
        print_ou_tree $ou_id "$new_prefix"
    done
}

echo "Organization Structure and SCPs:"
echo "================================"

root_id=$(aws organizations list-roots --query "Roots[0].Id" --output text)
echo -e "\nRoot"
echo "Attached SCPs: $(get_scps $root_id)"
print_ou_tree $root_id ""
' > scp_before.txt

私の環境では、以下の出力結果が得られました。

scp_before.txt
Organization Structure and SCPs:
================================

Root
Attached SCPs: FullAWSAccess,s3-public-block
├── A_OU
│   Attached SCPs: FullAWSAccess
├── B_OU
│   Attached SCPs: FullAWSAccess
└── C_OU
    Attached SCPs: FullAWSAccess,region-restrict
    ├── test2_OU
    │   Attached SCPs: FullAWSAccess
    │   └── test2-1_OU
    │       Attached SCPs: FullAWSAccess,deny-public-subnet
    └── test1_OU
        Attached SCPs: FullAWSAccess,deny-public-rds-instances
        └── test1-2_OU
            Attached SCPs: FullAWSAccess,deny-Bedrock
            └── test1-2-3_OU
                Attached SCPs: FullAWSAccess
                └── test1-2-3-4_OU
                    Attached SCPs: FullAWSAccess

SCPのデタッチ後に必要な作業を実施し、SCPを再度アタッチします。その後、設定を確認するため、以下のコマンドを実行します。

出力結果は、scp_after.txtファイルに保存します。

bash -c '
get_scps() {
    # SCPのリストを取得し、カンマ区切りに変換
    aws organizations list-policies-for-target --target-id $1 --filter SERVICE_CONTROL_POLICY --query "Policies[*].Name" --output text | tr "\t" ", "
}

print_ou_tree() {
    local parent_id=$1 prefix=$2
    local ous=$(aws organizations list-organizational-units-for-parent --parent-id $parent_id --query "OrganizationalUnits[*].[Id,Name]" --output text)
    local total_ous=$(echo "$ous" | wc -l)
    local current_ou=0

    echo "$ous" | while read -r ou_id ou_name; do
        [ -z "$ou_id" ] && continue
        ((current_ou++))

        # 現在の親OUの直下にある最後の子OUかどうかを判断
        if [ $current_ou -eq $total_ous ]; then
            local branch="└──" # 現在の階層レベルの最後の子OU
            local new_prefix="$prefix    "
        else
            local branch="├──" # 現在の階層レベルの中間の子OU
            local new_prefix="$prefix│   "
        fi

        # OUの名前を表示
        echo "$prefix$branch $ou_name"

        # SCPを取得して表示
        local scps=$(get_scps $ou_id)
        echo "$new_prefix""Attached SCPs: ${scps:-None}"

        # 子OUを再帰的に処理
        print_ou_tree $ou_id "$new_prefix"
    done
}

echo "Organization Structure and SCPs:"
echo "================================"

root_id=$(aws organizations list-roots --query "Roots[0].Id" --output text)
echo -e "\nRoot"
echo "Attached SCPs: $(get_scps $root_id)"
print_ou_tree $root_id ""
' > scp_after.txt

以下のコマンドを実行して、SCP設定の差分を確認します。差分がない場合(設定が完全に一致する場合)は、何も表示されません。

$ diff scp_before.txt scp_after.txt
$ 

差分がある場合は、以下のような形式で表示されます

< 変更前の行
> 変更後の行

SCPの設定確認の手順は以下の通りです。

  1. 上記のコマンドを実行して出力結果を保存する
  2. SCPをデタッチし、必要な作業を実施する
  3. SCPを再アタッチした後、上記のコマンドを実行する
  4. 出力結果の差分を確認し完全に一致することを確認する
    (一致を確認できれば、SCPが正しく再アタッチされたことを意味します)

SCPの変更は、組織全体に影響を与える可能性があるため、慎重に行う必要があります。

本記事で紹介したコマンドを使用することで、SCPの一時的なデタッチと再アタッチのプロセスをより安全に管理できます。

SCPの変更は組織全体のセキュリティに影響を与える重要な操作です。そのため、変更前後の状態を確認し、意図した通りの設定が適用されていることを確認してましょう。

参考

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/organizations/list-organizational-units-for-parent.html

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/organizations/list-policies-for-target.html

https://docs.aws.amazon.com/ja_jp/organizations/latest/userguide/orgs_getting-started_concepts.html

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.